import logging
import random
import re
from info.libs.yuntongxun.sms import CCP
from info.modules.models import User
from . import password_blue
from flask import current_app, jsonify, make_response, request, abort, session
from info.modules import constants
from info import redis_store, db
from info.utils.captcha.captcha.captcha import captcha
from info.utils.response_code import RET


# 创建所需要的视图函数
# 创建获取图片验证码的路由
@password_blue.route('/image_code')
def get_image_code():
    '''
    生成图片验证码的四个步骤
    获取参数，也就是获取验证码的id，id由前端生成
    利用第三方软件生成验证码图片和验证码的text，以及name文件
    将验证码保存到redis中，键要使用获取的id。这样可以保证唯一性。
    将数据返回给前端，就是验证码的图片文件

    '''
    # 获取参数，就是从请求里面获取数据图片id， 就是image_code_id
    image_code_id = request.args.get('image_code_id')
    # 在这里判断，如果为空，我们就直接抛出403错误
    if not image_code_id:
        return abort(403)

    # 不为空，我们就使用第三方库生成验证码
    name, text, image_data = captcha.generate_captcha()
    # print('#' * 50)
    # print(image_data)

    # 我们将数据加到redis里面，只要是对数据库的操作，我们都需要捕获异常
    try:
        redis_store.setex('image_code_id_' + image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)
    except Exception as E:
        # 对于数据库保存过程中所发生的错误，我们需要将他记录到日志中
        logging.error(E)
        return abort(500)

    # 将图片数据返回给浏览器，使用make_response返回。
    response = make_response(image_data)
    # 在这里我们要将接收的类型改为 image/png
    response.headers['Content-Type'] = 'image/jpg'
    return response


'''
    我们在这里定义这个函数的一些基本属性
    url：/sms_code
    请求方式：POST
    参数：image_code,image_code_id, mobile
    返回:json数据格式
    接收:json数据格式
'''


@password_blue.route('/sms_code', methods=["POST"])
def sms_code():
    '''
    获取到短信验证码的六个步骤
    获取参数,参数主要包括,image_code_id 用于对比redis库的数据的键的,image_code用于
    对比值的,mobile是用来接收短信的.
    将键值对和数据库中的键值对进行对比,
    对比成功,自己生成验证码,
    将验证码存放到数据库,并用唯一的键去标记它
    发送短信,使用第三方平台进行发送

    :return:
    '''

    # 获取参数,因为我们是用json接受的数据所以我们使用json来接收
    # 在开发中我们可以使用request.json来获取数据,可以一步到位的获取到json字典
    json_data = request.json
    mobile = json_data.get('mobile')
    image_code = json_data.get('image_code')
    image_code_id = json_data.get('image_code_id')

    # 获取到三个参数之后，我们就对这三个参数进行校验
    if not all([image_code_id, image_code, mobile]):
        # all表示只要是其中有一个数据是空的整个表达式就是假，就会进入这里
        # 返回的时候，我们返回json数据，也就是使用jsonify返回
        return jsonify(errno=RET.PARAMERR, errmsg='数据输入不完整')
    # 到这里证明数据是完整的，我们需要检验手机号码是否是正确的手机号码
    # 使用正则表达式来检验
    if not re.match(r'1[34578][0-9]{9}', mobile):
        # 进到这里说明数据格式不正确
        return jsonify(errno=RET.PARAMERR, errmsg='手机号输入不正确')

    # 如果输入全部正确，
    # 我们就需要从数据库调取调取数据，和用户输入的数据进行对比
    # 操作数据库我们需要捕获异常
    try:
        redis_store_code = redis_store.get('image_code_id_' + image_code_id)
    except Exception as e:
        # 用日志记录
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库错误')

    # 判断取得的redis——store——code是否是空值，如果是空，则表示数据已经过期了
    if not redis_store_code:
        return jsonify(errno=RET.DATAERR, errmsg='验证码已经过期')

    # image_code = bytes(image_code)
    redis_store_code = str(redis_store_code, encoding='utf-8')
    # 将获取到的验证码与数据库的验证码进行对比
    if image_code.lower() != redis_store_code.lower():
        # 进来则说明不相等
        return jsonify(errno=RET.DATAERR, errmsg='验证码填写错误')

    # 如果全部通过了，则可以生成短信验证码了
    sms_code_str = '%06d' % random.randint(0, 999999)

    print(sms_code_str)

    # 将验证码保存到数据库中，需要捕获异常
    try:
        redis_store.setex('sms_code' + mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code_str)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据存储失败')

    # 存储完成之后我们进行发送短信
    result = CCP().send_template_sms(mobile, [sms_code_str, 5], 1)
    if result != '000000':
        return jsonify(errno=RET.THIRDERR, errmsg='第三方发生错误')

    # 如果全部通过，我们返回OK
    return jsonify(errno=RET.OK, errmsg='短信发送成功')


# 实现注册模块
@password_blue.route('/register', methods=['GET', 'POST'])
def register():
    '''
    实现接口一般步骤
    获取参数，
        1：手机号，短信验证码， 密码
    校验参数
        1：校验数据完整性，就是有没有空的
        2：校验密码是否合格
    逻辑处理
        1： 校验短信验证码是否是和数据库里面的一样，
        2： 检验用户是否注册过
        4： 创建模板
        2： 将用户的信息提交到数据库中，
        注册用户，
        设置session保存用户数据
    返回数据
        1： 返回登录成功的消息
    '''
    # 获取json参数
    json_data = request.json
    # 从json中取出数据，分别是电话号，短信验证码，密码
    mobile = json_data.get('mobile')
    password = json_data.get('password')
    sms_code_ = json_data.get('sms_code')

    # 检验完整性
    if not all([mobile, password, sms_code_]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数不完整，求补全')
    # TODO 这里可以加一个密码的校验
    if not re.match(r'1[34578][0-9]{9}', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg='手机号输入错误')
    # 如果全部通过，我们对比短信验证码
    # 先从redis中去短信验证码
    try:
        redis_store_sms_code = redis_store.get('sms_code' + mobile)
        print(redis_store_sms_code)
    except Exception as e:
        # 用日志记录
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库读取失败')
    # 判断是否为空
    if not redis_store_sms_code:
        return jsonify(errno=RET.DBERR, errmsg='验证码已经过期，或者手机号有误')
    # 如果不为空，将两次的数据进行比较
    redis_store_sms_code = str(redis_store_sms_code, encoding='utf-8')
    if not redis_store_sms_code == sms_code_:
        return jsonify(errno=RET.DATAERR, errmsg='短信验证码错误')

    # 比较之后，将验证码删除
    try:
        redis_store.delete('sms_code' + mobile)
    except Exception as e:
        # 用日志记录
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='删除失败')
    # 判断用户是否已经在数据库中
    # 读取数据库需要捕获异常
    try:
        user = User.query.filter_by(mobile=mobile).first()

    except Exception as e:
        # 用日志记录
        print(e)
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='读取数据库失败')
    if user:
        return jsonify(errno=RET.DATAEXIST, errmsg='用户已经注册过啦，去登录吧')
    # 创建模板，并将数据存进去
    # 创建模板的时候，我们要确定模板中，有哪几个字段是不允许为空的，都填上
    # 首先创建模板的对象
    user_model = User()
    user_model.nick_name = mobile
    user_model.mobile = mobile
    # 这里的这个密码需要注意，我们需要调用model里面的方法进行加密
    '''
    @property
    def password(self):
        raise AttributeError("当前属性不可读")

    @password.setter
    def password(self, value):
        self.password_hash = generate_password_hash(value)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)
    # 这段代码是调用了@property方法，它就等于property.getter（）
    # 保存密码是调用了password.setter方法,这个方法是调用了加密密码的方法,
    # 至于为什么要这样加密,不在视图函数内直接加密,是因为添加模型,给密码加密,
    # 不应该放在逻辑处理的视图函数内,应该放在模板创建的地方
    '''
    user_model.password = password

    # 将字段的值添加好后，我们就开始想数据库提交这个注册用户的数据
    try:
        db.session.add(user_model)
        db.session.commit()
    except Exception as e:
        # 对于关系型数据库的操作，我们需要回滚
        db.session.rollback()
        # 用日志记录
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据存储错误')

    # 添加成功之后，我们需要添加一个session状态，
    # 如果有session就表示用户是登陆的，没有session就要求用户再次登录

    try:
        session['nick_name'] = mobile
        session['user_id'] = user_model.id
    except Exception as e:
        # 用日志记录
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据错误')
    # 存储用户的id值是为了判断等到下次用户登录的时候，我们去判断用户是否还存在
    # 因为有时候虽然session还保存着用户登录的状态，但是用户已经被删除了。有点扯，但是必须得这样

    # 到这里我们注册就结束了。
    return jsonify(errno=RET.OK, errmsg='成功')


# 这里我们开始写登录
# 登录的逻辑和注册的逻辑差不多，
@password_blue.route('/login', methods=['GET', 'POST'])
def login():
    '''
    1: 首先我们先获取数据
        获取mobile，和password
    2: 校验数据是否完整，
        校验电话是否符合格式
        校验密码是否符合格式

    3：逻辑处理
        查询用户是否注册过
        查询密码是否正确

    4：设置session，保持登录状态

    :return:
    '''
    # 获取数据,通过request.json来获取
    json_data = request.json
    mobile = json_data.get('mobile')
    password = json_data.get('password')
    # 创建模型对象
    user = User()
    # 校验完整性
    if not all([mobile, password]):
        return jsonify(errno=RET.DATAERR, errmsg='错误')

    # 校验手机号是否符合格式
    if not re.match(r'1[34578][0-9]{9}', mobile):
        # 进到这里说明数据格式不正确
        return jsonify(errno=RET.PARAMERR, errmsg='手机号输入不正确')

    # TODO 这里可以添加一个验证密码

    # 开始验证用户是否注册过
    try:
        check_mobile = User.query.filter(User.mobile == mobile).first()

    except Exception as e:
        # 用日志记录
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据库读取错误')

    if not check_mobile or not check_mobile.check_passowrd(password):
        return jsonify(errno=RET.DATAEXIST, errmsg='用户名或者密码错误')

    # 记录用户的登录状态
    session['nick_name'] = mobile
    session['user_id'] = check_mobile.id
    return jsonify(errno=RET.OK, errmsg='成功')


# 这里开始写登出系统
# @password_blue.route('/logout', methods=['POST'])
# def login_out():
#     # 登出的操作就是将数据库里面的session删除掉
#     # 就直接redis里面的固态
#     session.pop('user_id')
#     session.pop('nick_name')
#
#     return jsonify(errno=RET.OK, errmsg='登出成功')


@password_blue.route("/logout", methods=['POST'])
def logout():
    """
    清除session中的对应登录之后保存的信息
    :return:
    """
    session.pop('user_id', None)
    session.pop('nick_name', None)
    session.pop('mobile', None)
    session.pop('is_admin', None)

    # 返回结果
    return jsonify(errno=RET.OK, errmsg="OK")










